#include <iostream>
#include <algorithm>

#include "optimized_vector.h"

optimized_vector::optimized_vector(size_t size) : size_(size), capacity_(1)
{
   if (size_ <= 1)
   {
      data_.number = 0;
   } 
   else
   {
      capacity_ = calculate_capacity(size_);
      data_.array = new int[capacity_];
      for (size_t i = 0; i < size_; i++)
      {
         data_.array[i] = 0;
      }
   }
}

void optimized_vector::copy_data(const optimized_vector& a)
{
   if (size_ <= 1)
   {
      data_.number = a.data_.number;
   } 
   else if (capacity_ > 1)  
   {
      data_.array = new int[capacity_];
      for (size_t i = 0; i < size_; i++)
      {
         data_.array[i] =  a.data_.array[i];
      }
   }
}

optimized_vector::optimized_vector(const optimized_vector& a): size_(a.size_), capacity_(a.capacity_)
{   
   copy_data(a);
}

optimized_vector::~optimized_vector()
{
   if (capacity_ > 1)
   {
      delete[] data_.array;
   }
}

optimized_vector& optimized_vector::operator =(const optimized_vector& a)
{
   this->~optimized_vector();
   size_ = a.size_;
   capacity_ = a.capacity_;
   copy_data(a);
   return *this;
}

void optimized_vector::push_back(int value)
{
   int new_capacity = calculate_capacity(size_ + 1);
   resize_if_necessary(new_capacity);
   (*this)[size_++] = value;
}

int optimized_vector::pop_back()
{
   int new_capacity = calculate_capacity(size_ -1);
   int buf = (*this)[size_ - 1];
   resize_if_necessary(new_capacity);
   size_--;
   return buf;
}

void optimized_vector::resize_if_necessary(size_t new_capacity)
{
   if (new_capacity != capacity_)
   {
      resize_data(new_capacity);
   }
}

size_t optimized_vector::size() const
{
   return size_;
}

void optimized_vector::resize_data(size_t new_capacity)
{
   if (new_capacity == 1)
   {
      int buffer = (*this)[0];
      delete[] data_.array;
      data_.number = buffer;
      
   }
   else
   {
      int* buffer = new int[new_capacity];
      for (size_t i = 0; i < size_; i++)
      {
         buffer[i] = (*this)[i];
      }
      if (capacity_ > 1)
      {
         delete[] data_.array;
      }
      data_.array = buffer;
   }
   capacity_ = new_capacity;
}

size_t optimized_vector::calculate_capacity(size_t new_size) const
{
   size_t ans = capacity_;
   if (new_size < 2)
   {
      return 1;
   }
   while (ans < new_size)
   {
      ans *= VECTOR_RATIO;
   }
   if (new_size * VECTOR_RATIO * VECTOR_RATIO == ans)
   {
      ans /= VECTOR_RATIO;
   }
   return ans;
}

const int optimized_vector::operator[](size_t index) const
{
   if (capacity_ == 1)
   {
      return data_.number;
   }
   else
   {
      return data_.array[index];
   }
}

int& optimized_vector::operator[](size_t index)
{
   if (capacity_ == 1)
   {
      return data_.number;
   }
   else
   {
      return data_.array[index];
   }
}

bool optimized_vector::operator ==(const optimized_vector& a) const
{
   if (size() != a.size())
   {
      return false;
   }
   for (size_t i = 0; i < size_; i++)
   {
      if ((*this)[i] != a[i])
      {
         return false;
      }
   }
   return true;
}

bool optimized_vector::operator !=(const optimized_vector& a) const
{
   return !((*this) == a);
}

void optimized_vector::swap(optimized_vector& a)
{
    std::swap(size_, a.size_);
    std::swap(capacity_, a.capacity_);
    std::swap(data_, a.data_);
}

void optimized_vector::reverse()
{
   if (capacity_ > 1)
   {
      for (size_t i = 0; i < size_ / 2; i++)
      {
         std::swap((*this)[i], (*this)[size_ - 1 - i]);
      }
   }
}

void optimized_vector::resize( size_t new_size )
{
   if (new_size < size_)
   {
      for (size_t i = new_size; i < size_; i++)
      {
         pop_back();
      }
      return;
   }
   if (new_size > size_)
   {
      for (size_t i = size_; i < new_size; i++)
      {
         push_back(0);
      }
      return;
   }
}

int optimized_vector::back() const
{
   return (*this)[size_ - 1];
}


